home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / unix / xyzmodem / rz.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-07-16  |  23.9 KB  |  1,202 lines

  1. #define VERSION "1.21 05-16-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -LARGE -Ox -K -i % -o rz; size rz;
  5. <-xtx-*> cc386 -Ox rz.c -o $B/rz;  size386 $B/rz
  6.  *
  7.  * rz.c By Chuck Forsberg
  8.  *
  9.  *    cc -O rz.c -o rz        USG (3.0) Unix
  10.  *     cc -O -DV7  rz.c -o rz        Unix V7, BSD 2.8 - 4.3
  11.  *
  12.  *    ln rz rb;  ln rz rx            For either system
  13.  *
  14.  *    ln rz /usr/bin/rzrmail        For remote mail.  Make this the
  15.  *                    login shell. rzrmail then calls
  16.  *                    rmail(1) to deliver mail.
  17.  *
  18.  *
  19.  *  Unix is a trademark of Western Electric Company
  20.  *
  21.  * A program for Unix to receive files and commands from computers running
  22.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
  23.  *  rz uses Unix buffered input to reduce wasted CPU time.
  24.  *
  25.  * Iff the program is invoked by rzCOMMAND, output is piped to 
  26.  * "COMMAND filename"
  27.  *
  28.  *  Some systems (Venix, Coherent, Regulus) may not support tty raw mode
  29.  *  read(2) the same way as Unix. ONEREAD must be defined to force one
  30.  *  character reads for these systems. Added 7-01-84 CAF
  31.  *
  32.  *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
  33.  *
  34.  *  NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
  35.  *  doesn't work properly (even though it compiles without error!),
  36.  *
  37.  *  HOWMANY should be tuned for best performance
  38.  *
  39.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  40.  */
  41. #define LOGFILE "/tmp/rzlog"
  42. #define zperr vfile
  43.  
  44. #include <stdio.h>
  45. #include <signal.h>
  46. #include <setjmp.h>
  47. #include <ctype.h>
  48. FILE *popen();
  49.  
  50. #define OK 0
  51. #define FALSE 0
  52. #define TRUE 1
  53. #define ERROR (-1)
  54.  
  55. /*
  56.  * Max value for HOWMANY is 255.
  57.  *   A larger value reduces system overhead but may evoke kernel bugs.
  58.  *   133 corresponds to an XMODEM/CRC sector
  59.  */
  60. #ifndef HOWMANY
  61. #define HOWMANY 133
  62. #endif
  63.  
  64. int Zmodem=0;        /* ZMODEM protocol requested */
  65. int Nozmodem = 0;    /* If invoked as "rb" */
  66. unsigned Baudrate;
  67. #include "rbsb.c"    /* most of the system dependent stuff here */
  68.  
  69. char *substr();
  70. FILE *fout;
  71.  
  72. /*
  73.  * Routine to calculate the free bytes on the current file system
  74.  *  ~0 means many free bytes (unknown)
  75.  */
  76. long getfree()
  77. {
  78.     return(~0L);    /* many free bytes ... */
  79. }
  80.  
  81. /* Ward Christensen / CP/M parameters - Don't change these! */
  82. #define ENQ 005
  83. #define CAN ('X'&037)
  84. #define XOFF ('s'&037)
  85. #define XON ('q'&037)
  86. #define SOH 1
  87. #define STX 2
  88. #define EOT 4
  89. #define ACK 6
  90. #define NAK 025
  91. #define CPMEOF 032
  92. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  93. #define TIMEOUT (-2)
  94. #define RCDO (-3)
  95. #define ERRORMAX 5
  96. #define RETRYMAX 5
  97. #define WCEOT (-10)
  98. #define SECSIZ 128    /* cp/m's Magic Number record size */
  99. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  100. #define KSIZE 1024    /* record size with k option */
  101. #define UNIXFILE 0x8000    /* happens to the the S_IFREG file mask bit for stat */
  102.  
  103. int Lastrx;
  104. int Crcflg;
  105. int Firstsec;
  106. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  107. int errors;
  108. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  109. #ifdef ONEREAD
  110. /* Sorry, Regulus and some others don't work right in raw mode! */
  111. int Readnum = 1;    /* Number of bytes to ask for in read() from modem */
  112. #else
  113. int Readnum = HOWMANY;    /* Number of bytes to ask for in read() from modem */
  114. #endif
  115.  
  116. #define DEFBYTL 2000000000L    /* default rx file size */
  117. long Bytesleft;        /* number of bytes of incoming file left */
  118. long Modtime;        /* Unix style mod time for incoming file */
  119. short Filemode;        /* Unix style mode for incoming file */
  120. char Pathname[PATHLEN];
  121. char *Progname;        /* the name by which we were called */
  122.  
  123. int Batch=0;
  124. int Wcsmask=0377;
  125. int Topipe=0;
  126. int MakeLCPathname=TRUE;    /* make received pathname lower case */
  127. int Verbose=0;
  128. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  129. int Nflag = 0;        /* Don't really transfer files */
  130. int Rxbinary=FALSE;    /* receive all files in bin mode */
  131. int Rxascii=FALSE;    /* receive files in ascii (translate) mode */
  132. int Thisbinary;        /* current file is to be received in bin mode */
  133. int Blklen;        /* record length of received packets */
  134. char secbuf[KSIZE];
  135. char linbuf[HOWMANY];
  136. int Lleft=0;        /* number of characters in linbuf */
  137. time_t timep[2];
  138. char Lzmanag;        /* Local file management request */
  139. char zconv;        /* ZMODEM file conversion request */
  140. char zmanag;        /* ZMODEM file management request */
  141. char ztrans;        /* ZMODEM file transport request */
  142. int Zctlesc;        /* Encode control characters */
  143. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  144.  
  145. jmp_buf tohere;        /* For the interrupt on RX timeout */
  146.  
  147. #include "zm.c"
  148.  
  149. int tryzhdrtype=ZRINIT;    /* Header type to send corresponding to Last rx close */
  150.  
  151. alrm()
  152. {
  153.     longjmp(tohere, -1);
  154. }
  155.  
  156. /* called by signal interrupt or terminate to clean things up */
  157. bibi(n)
  158. {
  159.     if (Zmodem)
  160.         zmputs(Attn);
  161.     canit(); mode(0);
  162.     fprintf(stderr, "rz: caught signal %d; exiting", n);
  163.     exit(128+n);
  164. }
  165.  
  166. main(argc, argv)
  167. char *argv[];
  168. {
  169.     register char *cp;
  170.     register npats;
  171.     char *virgin, **patts;
  172.     char *getenv();
  173.     int exitcode;
  174.  
  175.     Rxtimeout = 100;
  176.     setbuf(stderr, NULL);
  177.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  178.         Restricted=TRUE;
  179.  
  180.     chkinvok(virgin=argv[0]);    /* if called as [-]rzCOMMAND set flag */
  181.     npats = 0;
  182.     while (--argc) {
  183.         cp = *++argv;
  184.         if (*cp == '-') {
  185.             while( *++cp) {
  186.                 switch(*cp) {
  187.                 case '+':
  188.                     Lzmanag = ZMAPND; break;
  189.                 case '1':
  190.                     iofd = 1; break;
  191.                 case '7':
  192.                     Wcsmask = 0177;
  193.                 case 'a':
  194.                     Rxascii=TRUE;  break;
  195.                 case 'b':
  196.                     Rxbinary=TRUE; break;
  197.                 case 'c':
  198.                     Crcflg=TRUE; break;
  199.                 case 'D':
  200.                     Nflag = TRUE; break;
  201.                 case 'e':
  202.                     Zctlesc = 1; break;
  203.                 case 'p':
  204.                     Lzmanag = ZMPROT;  break;
  205.                 case 'q':
  206.                     Quiet=TRUE; Verbose=0; break;
  207.                 case 't':
  208.                     if (--argc < 1) {
  209.                         usage();
  210.                     }
  211.                     Rxtimeout = atoi(*++argv);
  212.                     if (Rxtimeout<10 || Rxtimeout>1000)
  213.                         usage();
  214.                     break;
  215.                 case 'w':
  216.                     if (--argc < 1) {
  217.                         usage();
  218.                     }
  219.                     Zrwindow = atoi(*++argv);
  220.                     break;
  221.                 case 'u':
  222.                     MakeLCPathname=FALSE; break;
  223.                 case 'v':
  224.                     ++Verbose; break;
  225.                 default:
  226.                     usage();
  227.                 }
  228.             }
  229.         }
  230.         else if ( !npats && argc>0) {
  231.             if (argv[0][0]) {
  232.                 npats=argc;
  233.                 patts=argv;
  234.             }
  235.         }
  236.     }
  237.     if (npats > 1)
  238.         usage();
  239.     if (Verbose) {
  240.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  241.             printf("Can't open log file %s\n",LOGFILE);
  242.             exit(0200);
  243.         }
  244.         setbuf(stderr, NULL);
  245.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  246.     }
  247.     if (fromcu() && !Quiet) {
  248.         if (Verbose == 0)
  249.             Verbose = 2;
  250.     }
  251.     mode(1);
  252.     if (signal(SIGINT, bibi) == SIG_IGN) {
  253.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  254.     }
  255.     else {
  256.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  257.     }
  258.     signal(SIGTERM, bibi);
  259.     if (wcreceive(npats, patts)==ERROR) {
  260.         exitcode=0200;
  261.         canit();
  262.     }
  263.     mode(0);
  264.     if (exitcode && !Zmodem)    /* bellow again with all thy might. */
  265.         canit();
  266.     exit(exitcode);
  267. }
  268.  
  269.  
  270. usage()
  271. {
  272.     fprintf(stderr,"%s %s for %s by Chuck Forsberg\n",
  273.       Progname, VERSION, OS);
  274.     fprintf(stderr,"Usage:    rz [-1abeuv]        (ZMODEM Batch)\n");
  275.     fprintf(stderr,"or    rb [-1abuv]        (YMODEM Batch)\n");
  276.     fprintf(stderr,"or    rx [-1abcv] file    (XMODEM or XMODEM-1k)\n");
  277.     fprintf(stderr,"      -1 For cu(1): Use fd 1 for input\n");
  278.     fprintf(stderr,"      -a ASCII transfer (strip CR)\n");
  279.     fprintf(stderr,"      -b Binary transfer for all files\n");
  280.     fprintf(stderr,"      -c Use 16 bit CRC    (XMODEM)\n");
  281.     fprintf(stderr,"      -e Ignore control characters    (ZMODEM)\n");
  282.     fprintf(stderr,"      -v Verbose more v's give more info\n");
  283.     exit(1);
  284. }
  285. /*
  286.  *  Debugging information output interface routine
  287.  */
  288. /* VARARGS1 */
  289. vfile(f, a, b, c)
  290. register char *f;
  291. {
  292.     if (Verbose > 1) {
  293.         fprintf(stderr, f, a, b, c);
  294.         fprintf(stderr, "\n");
  295.     }
  296. }
  297.  
  298. /*
  299.  * Let's receive something already.
  300.  */
  301.  
  302. char *rbmsg =
  303. "%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n";
  304.  
  305. wcreceive(argc, argp)
  306. char **argp;
  307. {
  308.     register c;
  309.  
  310.     if (Batch || argc==0) {
  311.         Crcflg=(Wcsmask==0377);
  312.         if ( !Quiet)
  313.             fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
  314.         if (c=tryz()) {
  315.             if (c == ZCOMPL)
  316.                 return OK;
  317.             if (c == ERROR)
  318.                 goto fubar;
  319.             c = rzfiles();
  320.             if (c)
  321.                 goto fubar;
  322.         } else {
  323.             for (;;) {
  324.                 if (wcrxpn(secbuf)== ERROR)
  325.                     goto fubar;
  326.                 if (secbuf[0]==0)
  327.                     return OK;
  328.                 if (procheader(secbuf) == ERROR)
  329.                     goto fubar;
  330.                 if (wcrx()==ERROR)
  331.                     goto fubar;
  332.             }
  333.         }
  334.     } else {
  335.         Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  336.  
  337.         procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
  338.         fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
  339.         if ((fout=fopen(Pathname, "w")) == NULL)
  340.             return ERROR;
  341.         if (wcrx()==ERROR)
  342.             goto fubar;
  343.     }
  344.     return OK;
  345. fubar:
  346.     canit();
  347.     if (Topipe && fout) {
  348.         pclose(fout);  return ERROR;
  349.     }
  350.     if (fout)
  351.         fclose(fout);
  352.     if (Restricted) {
  353.         unlink(Pathname);
  354.         fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
  355.     }
  356.     return ERROR;
  357. }
  358.  
  359.  
  360. /*
  361.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  362.  * Length is indeterminate as long as less than Blklen
  363.  * A null string represents no more files (YMODEM)
  364.  */
  365. wcrxpn(rpn)
  366. char *rpn;    /* receive a pathname */
  367. {
  368.     register c;
  369.  
  370. #ifdef NFGVMIN
  371.     readline(1);
  372. #else
  373.     purgeline();
  374. #endif
  375.  
  376. et_tu:
  377.     Firstsec=TRUE;  Eofseen=FALSE;
  378.     sendline(Crcflg?WANTCRC:NAK);
  379.     Lleft=0;    /* Do read next time ... */
  380.     while ((c = wcgetsec(rpn, 100)) != 0) {
  381.         log( "Pathname fetch returned %d\n", c);
  382.         if (c == WCEOT) {
  383.             sendline(ACK);
  384.             Lleft=0;    /* Do read next time ... */
  385.             readline(1);
  386.             goto et_tu;
  387.         }
  388.         return ERROR;
  389.     }
  390.     sendline(ACK);
  391.     return OK;
  392. }
  393.  
  394. /*
  395.  * Adapted from CMODEM13.C, written by
  396.  * Jack M. Wierda and Roderick W. Hart
  397.  */
  398.  
  399. wcrx()
  400. {
  401.     register int sectnum, sectcurr;
  402.     register char sendchar;
  403.     register char *p;
  404.     int cblklen;            /* bytes to dump this block */
  405.  
  406.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  407.     sendchar=Crcflg?WANTCRC:NAK;
  408.  
  409.     for (;;) {
  410.         sendline(sendchar);    /* send it now, we're ready! */
  411.         Lleft=0;    /* Do read next time ... */
  412.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  413.         report(sectcurr);
  414.         if (sectcurr==(sectnum+1 &Wcsmask)) {
  415.             sectnum++;
  416.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  417.             if (putsec(secbuf, cblklen)==ERROR)
  418.                 return ERROR;
  419.             if ((Bytesleft-=cblklen) < 0)
  420.                 Bytesleft = 0;
  421.             sendchar=ACK;
  422.         }
  423.         else if (sectcurr==(sectnum&Wcsmask)) {
  424.             log( "Received dup Sector\n");
  425.             sendchar=ACK;
  426.         }
  427.         else if (sectcurr==WCEOT) {
  428.             if (closeit())
  429.                 return ERROR;
  430.             sendline(ACK);
  431.             Lleft=0;    /* Do read next time ... */
  432.             return OK;
  433.         }
  434.         else if (sectcurr==ERROR)
  435.             return ERROR;
  436.         else {
  437.             log( "Sync Error\n");
  438.             return ERROR;
  439.         }
  440.     }
  441. }
  442.  
  443. /*
  444.  * Wcgetsec fetches a Ward Christensen type sector.
  445.  * Returns sector number encountered or ERROR if valid sector not received,
  446.  * or CAN CAN received
  447.  * or WCEOT if eot sector
  448.  * time is timeout for first char, set to 4 seconds thereafter
  449.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  450.  *    (Caller must do that when he is good and ready to get next sector)
  451.  */
  452.  
  453. wcgetsec(rxbuf, maxtime)
  454. char *rxbuf;
  455. int maxtime;
  456. {
  457.     register checksum, wcj, firstch;
  458.     register unsigned short oldcrc;
  459.     register char *p;
  460.     int sectcurr;
  461.  
  462.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  463.  
  464.         if ((firstch=readline(maxtime))==STX) {
  465.             Blklen=KSIZE; goto get2;
  466.         }
  467.         if (firstch==SOH) {
  468.             Blklen=SECSIZ;
  469. get2:
  470.             sectcurr=readline(1);
  471.             if ((sectcurr+(oldcrc=readline(1)))==Wcsmask) {
  472.                 oldcrc=checksum=0;
  473.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  474.                     if ((firstch=readline(1)) < 0)
  475.                         goto bilge;
  476.                     oldcrc=updcrc(firstch, oldcrc);
  477.                     checksum += (*p++ = firstch);
  478.                 }
  479.                 if ((firstch=readline(1)) < 0)
  480.                     goto bilge;
  481.                 if (Crcflg) {
  482.                     oldcrc=updcrc(firstch, oldcrc);
  483.                     if ((firstch=readline(1)) < 0)
  484.                         goto bilge;
  485.                     oldcrc=updcrc(firstch, oldcrc);
  486.                     if (oldcrc & 0xFFFF)
  487.                         log("CRC=0%o\n", oldcrc);
  488.                     else {
  489.                         Firstsec=FALSE;
  490.                         return sectcurr;
  491.                     }
  492.                 }
  493.                 else if (((checksum-firstch)&Wcsmask)==0) {
  494.                     Firstsec=FALSE;
  495.                     return sectcurr;
  496.                 }
  497.                 else
  498.                     log( "Checksum Error\n");
  499.             }
  500.             else
  501.                 log("Sector number garbled 0%o 0%o\n",
  502.                  sectcurr, oldcrc);
  503.         }
  504.         /* make sure eot really is eot and not just mixmash */
  505. #ifdef NFGVMIN
  506.         else if (firstch==EOT && readline(1)==TIMEOUT)
  507.             return WCEOT;
  508. #else
  509.         else if (firstch==EOT && Lleft==0)
  510.             return WCEOT;
  511. #endif
  512.         else if (firstch==CAN) {
  513.             if (Lastrx==CAN) {
  514.                 log( "Sender CANcelled\n");
  515.                 return ERROR;
  516.             } else {
  517.                 Lastrx=CAN;
  518.                 continue;
  519.             }
  520.         }
  521.         else if (firstch==TIMEOUT) {
  522.             if (Firstsec)
  523.                 goto humbug;
  524. bilge:
  525.             log( "Timeout\n");
  526.         }
  527.         else
  528.             log( "Got 0%o sector header\n", firstch);
  529.  
  530. humbug:
  531.         Lastrx=0;
  532.         while(readline(1)!=TIMEOUT)
  533.             ;
  534.         if (Firstsec) {
  535.             sendline(Crcflg?WANTCRC:NAK);
  536.             Lleft=0;    /* Do read next time ... */
  537.         } else {
  538.             maxtime=40; sendline(NAK);
  539.             Lleft=0;    /* Do read next time ... */
  540.         }
  541.     }
  542.     /* try to stop the bubble machine. */
  543.     canit();
  544.     return ERROR;
  545. }
  546.  
  547. /*
  548.  * This version of readline is reasoably well suited for
  549.  * reading many characters.
  550.  *  (except, currently, for the Regulus version!)
  551.  *
  552.  * timeout is in tenths of seconds
  553.  */
  554. readline(timeout)
  555. int timeout;
  556. {
  557.     register n;
  558.     static char *cdq;    /* pointer for removing chars from linbuf */
  559.  
  560.     if (--Lleft >= 0) {
  561.         if (Verbose > 8) {
  562.             fprintf(stderr, "%02x ", *cdq&0377);
  563.         }
  564.         return (*cdq++ & Wcsmask);
  565.     }
  566.     n = timeout/10;
  567.     if (n < 2)
  568.         n = 3;
  569.     if (Verbose > 3)
  570.         fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ",
  571.           n, Readnum);
  572.     if (setjmp(tohere)) {
  573. #ifdef TIOCFLUSH
  574. /*        ioctl(iofd, TIOCFLUSH, 0); */
  575. #endif
  576.         Lleft = 0;
  577.         if (Verbose>1)
  578.             fprintf(stderr, "Readline:TIMEOUT\n");
  579.         return TIMEOUT;
  580.     }
  581.     signal(SIGALRM, alrm); alarm(n);
  582.     Lleft=read(iofd, cdq=linbuf, Readnum);
  583.     alarm(0);
  584.     if (Verbose > 3) {
  585.         fprintf(stderr, "Read returned %d bytes\n", Lleft);
  586.     }
  587.     if (Lleft < 1)
  588.         return TIMEOUT;
  589.     --Lleft;
  590.     if (Verbose > 8) {
  591.         fprintf(stderr, "%02x ", *cdq&0377);
  592.     }
  593.     return (*cdq++ & Wcsmask);
  594. }
  595.  
  596.  
  597.  
  598. /*
  599.  * Purge the modem input queue of all characters
  600.  */
  601. purgeline()
  602. {
  603.     Lleft = 0;
  604. #ifdef USG
  605.     ioctl(iofd, TCFLSH, 0);
  606. #else
  607.     lseek(iofd, 0L, 2);
  608. #endif
  609. }
  610.  
  611.  
  612. /*
  613.  * Process incoming file information header
  614.  */
  615. procheader(name)
  616. char *name;
  617. {
  618.     register char *openmode, *p, **pp;
  619.  
  620.     /* set default parameters and overrides */
  621.     openmode = "w";
  622.     Thisbinary = (!Rxascii) || Rxbinary;
  623.     if (Lzmanag)
  624.         zmanag = Lzmanag;
  625.  
  626.     /*
  627.      *  Process ZMODEM remote file management requests
  628.      */
  629.     if (!Rxbinary && zconv == ZCNL)    /* Remote ASCII override */
  630.         Thisbinary = 0;
  631.     if (zconv == ZCBIN)    /* Remote Binary override */
  632.         Thisbinary = TRUE;
  633.     else if (zmanag == ZMAPND)
  634.         openmode = "a";
  635.     /* ZMPROT check for existing file */
  636.     if (zmanag == ZMPROT && (fout=fopen(name, "r"))) {
  637.         fclose(fout);  return ERROR;
  638.     }
  639.  
  640.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  641.  
  642.     p = name + 1 + strlen(name);
  643.     if (*p) {    /* file coming from Unix or DOS system */
  644.         sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
  645.         if (Filemode & UNIXFILE)
  646.             ++Thisbinary;
  647.         if (Verbose) {
  648.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  649.               name, Bytesleft, Modtime, Filemode);
  650.         }
  651.     }
  652.     else {        /* File coming from CP/M system */
  653.         for (p=name; *p; ++p)        /* change / to _ */
  654.             if ( *p == '/')
  655.                 *p = '_';
  656.  
  657.         if ( *--p == '.')        /* zap trailing period */
  658.             *p = 0;
  659.     }
  660.  
  661.     if (!Zmodem && MakeLCPathname && !IsAnyLower(name))
  662.         uncaps(name);
  663.     if (Topipe) {
  664.         sprintf(Pathname, "%s %s", Progname+2, name);
  665.         if (Verbose)
  666.             fprintf(stderr,  "Topipe: %s %s\n",
  667.               Pathname, Thisbinary?"BIN":"ASCII");
  668.         if ((fout=popen(Pathname, "w")) == NULL)
  669.             return ERROR;
  670.     } else {
  671.         strcpy(Pathname, name);
  672.         if (Verbose) {
  673.             fprintf(stderr,  "Receiving %s %s %s\n",
  674.               name, Thisbinary?"BIN":"ASCII", openmode);
  675.         }
  676.         checkpath(name);
  677.         if (Nflag)
  678.             name = "/dev/null";
  679.         if ((fout=fopen(name, openmode)) == NULL)
  680.             return ERROR;
  681.     }
  682.     return OK;
  683. }
  684.  
  685. /*
  686.  * Putsec writes the n characters of buf to receive file fout.
  687.  *  If not in binary mode, carriage returns, and all characters
  688.  *  starting with CPMEOF are discarded.
  689.  */
  690. putsec(buf, n)
  691. char *buf;
  692. register n;
  693. {
  694.     register char *p;
  695.  
  696.     if (Thisbinary) {
  697.         for (p=buf; --n>=0; )
  698.             putc( *p++, fout);
  699.     }
  700.     else {
  701.         if (Eofseen)
  702.             return OK;
  703.         for (p=buf; --n>=0; ++p ) {
  704.             if ( *p == '\r')
  705.                 continue;
  706.             if (*p == CPMEOF) {
  707.                 Eofseen=TRUE; return OK;
  708.             }
  709.             putc(*p ,fout);
  710.         }
  711.     }
  712.     return OK;
  713. }
  714.  
  715. /*
  716.  *  Send a character to modem.  Small is beautiful.
  717.  */
  718. sendline(c)
  719. {
  720.     char d;
  721.  
  722.     d = c;
  723.     if (Verbose>4)
  724.         fprintf(stderr, "Sendline: %x\n", c);
  725.     write(1, &d, 1);
  726. }
  727.  
  728. xsendline(c)
  729. {
  730.     sendline(c);
  731. }
  732.  
  733. flushmo() {}
  734.  
  735.  
  736.  
  737.  
  738. /* make string s lower case */
  739. uncaps(s)
  740. register char *s;
  741. {
  742.     for ( ; *s; ++s)
  743.         if (isupper(*s))
  744.             *s = tolower(*s);
  745. }
  746. /*
  747.  * IsAnyLower returns TRUE if string s has lower case letters.
  748.  */
  749. IsAnyLower(s)
  750. register char *s;
  751. {
  752.     for ( ; *s; ++s)
  753.         if (islower(*s))
  754.             return TRUE;
  755.     return FALSE;
  756. }
  757.  
  758. /*
  759.  * substr(string, token) searches for token in string s
  760.  * returns pointer to token within string if found, NULL otherwise
  761.  */
  762. char *
  763. substr(s, t)
  764. register char *s,*t;
  765. {
  766.     register char *ss,*tt;
  767.     /* search for first char of token */
  768.     for (ss=s; *s; s++)
  769.         if (*s == *t)
  770.             /* compare token with substring */
  771.             for (ss=s,tt=t; ;) {
  772.                 if (*tt == 0)
  773.                     return s;
  774.                 if (*ss++ != *tt++)
  775.                     break;
  776.             }
  777.     return NULL;
  778. }
  779.  
  780. /*
  781.  * Log an error
  782.  */
  783. /*VARARGS1*/
  784. log(s,p,u)
  785. char *s, *p, *u;
  786. {
  787.     if (!Verbose)
  788.         return;
  789.     fprintf(stderr, "error %d: ", errors);
  790.     fprintf(stderr, s, p, u);
  791. }
  792.  
  793. /* send cancel string to get the other end to shut up */
  794. canit()
  795. {
  796.     static char canistr[] = {
  797.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  798.     };
  799.  
  800.     printf(canistr);
  801.     Lleft=0;    /* Do read next time ... */
  802.     fflush(stdout);
  803. }
  804.  
  805.  
  806. /*
  807.  * Return 1 iff stdout and stderr are different devices
  808.  *  indicating this program operating with a modem on a
  809.  *  different line
  810.  */
  811. fromcu()
  812. {
  813.     struct stat a, b;
  814.     fstat(1, &a); fstat(2, &b);
  815.     return (a.st_rdev != b.st_rdev);
  816. }
  817.  
  818. report(sct)
  819. int sct;
  820. {
  821.     if (Verbose>1)
  822.         fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
  823. }
  824.  
  825. /*
  826.  * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
  827.  * If called as [-][dir/../]rzCOMMAND set the pipe flag
  828.  * If called as rb use YMODEM protocol
  829.  */
  830. chkinvok(s)
  831. char *s;
  832. {
  833.     register char *p;
  834.  
  835.     p = s;
  836.     while (*p == '-')
  837.         s = ++p;
  838.     while (*p)
  839.         if (*p++ == '/')
  840.             s = p;
  841.     if (*s == 'v') {
  842.         Verbose=1; ++s;
  843.     }
  844.     Progname = s;
  845.     if (s[0]=='r' && s[1]=='b')
  846.         Nozmodem = TRUE;
  847.     if (s[2] && s[0]=='r' && s[1]=='b')
  848.         Topipe=TRUE;
  849.     if (s[2] && s[0]=='r' && s[1]=='z')
  850.         Topipe=TRUE;
  851. }
  852.  
  853. /*
  854.  * Totalitarian Communist pathname processing
  855.  */
  856. checkpath(name)
  857. char *name;
  858. {
  859.     if (Restricted) {
  860.         if (fopen(name, "r") != NULL) {
  861.             canit();
  862.             fprintf(stderr, "\r\nrz: %s exists\n", name);
  863.             bibi();
  864.         }
  865.         /* restrict pathnames to current tree or uucppublic */
  866.         if ( substr(name, "../")
  867.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  868.             canit();
  869.             fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
  870.             bibi();
  871.         }
  872.     }
  873. }
  874.  
  875. /*
  876.  * Initialize for Zmodem receive attempt, try to activate Zmodem sender
  877.  *  Handles ZSINIT frame
  878.  *  Return ZFILE if Zmodem filename received, -1 on error,
  879.  *   ZCOMPL if transaction finished,  else 0
  880.  */
  881. tryz()
  882. {
  883.     register c, n;
  884.     register cmdzack1flg;
  885.  
  886.     if (Nozmodem)        /* Check for "rb" program name */
  887.         return 0;
  888.  
  889.  
  890.     for (n=Zmodem?10:5; --n>=0; ) {
  891.         /* Set buffer length (0) and capability flags */
  892.         stohdr(0L);
  893. #ifdef CANBREAK
  894.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
  895. #else
  896.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
  897. #endif
  898.         if (Zctlesc)
  899.             Txhdr[ZF0] |= TESCCTL;
  900.         zshhdr(tryzhdrtype, Txhdr);
  901. again:
  902.         switch (zgethdr(Rxhdr, 0)) {
  903.         case ZRQINIT:
  904.             continue;
  905.         case ZEOF:
  906.             continue;
  907.         case TIMEOUT:
  908.             continue;
  909.         case ZFILE:
  910.             zconv = Rxhdr[ZF0];
  911.             zmanag = Rxhdr[ZF1];
  912.             ztrans = Rxhdr[ZF2];
  913.             tryzhdrtype = ZRINIT;
  914.             c = zrdata(secbuf, KSIZE);
  915.             mode(3);
  916.             if (c == GOTCRCW)
  917.                 return ZFILE;
  918.             zshhdr(ZNAK, Txhdr);
  919.             goto again;
  920.         case ZSINIT:
  921.             Zctlesc = TESCCTL & Rxhdr[ZF0];
  922.             if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
  923.                 zshhdr(ZACK, Txhdr);
  924.                 goto again;
  925.             }
  926.             zshhdr(ZNAK, Txhdr);
  927.             goto again;
  928.         case ZFREECNT:
  929.             stohdr(getfree());
  930.             zshhdr(ZACK, Txhdr);
  931.             goto again;
  932.         case ZCOMMAND:
  933.             cmdzack1flg = Rxhdr[ZF0];
  934.             if (zrdata(secbuf, KSIZE) == GOTCRCW) {
  935.                 if (cmdzack1flg & ZCACK1)
  936.                     stohdr(0L);
  937.                 else
  938.                     stohdr((long)sys2(secbuf));
  939.                 purgeline();    /* dump impatient questions */
  940.                 do {
  941.                     zshhdr(ZCOMPL, Txhdr);
  942.                 }
  943.                 while (++errors<10 && zgethdr(Rxhdr,1) != ZFIN);
  944.                 ackbibi();
  945.                 if (cmdzack1flg & ZCACK1)
  946.                     exec2(secbuf);
  947.                 return ZCOMPL;
  948.             }
  949.             zshhdr(ZNAK, Txhdr); goto again;
  950.         case ZCOMPL:
  951.             goto again;
  952.         default:
  953.             continue;
  954.         case ZFIN:
  955.             ackbibi(); return ZCOMPL;
  956.         case ZCAN:
  957.             return ERROR;
  958.         }
  959.     }
  960.     return 0;
  961. }
  962.  
  963. /*
  964.  * Receive 1 or more files with ZMODEM protocol
  965.  */
  966. rzfiles()
  967. {
  968.     register c;
  969.  
  970.     for (;;) {
  971.         switch (c = rzfile()) {
  972.         case ZEOF:
  973.         case ZSKIP:
  974.             switch (tryz()) {
  975.             case ZCOMPL:
  976.                 return OK;
  977.             default:
  978.                 return ERROR;
  979.             case ZFILE:
  980.                 break;
  981.             }
  982.             continue;
  983.         default:
  984.             return c;
  985.         case ERROR:
  986.             return ERROR;
  987.         }
  988.     }
  989. }
  990.  
  991. /*
  992.  * Receive a file with ZMODEM protocol
  993.  *  Assumes file name frame is in secbuf
  994.  */
  995. rzfile()
  996. {
  997.     register c, n;
  998.     long rxbytes;
  999.  
  1000.     Eofseen=FALSE;
  1001.     if (procheader(secbuf) == ERROR) {
  1002.         return (tryzhdrtype = ZSKIP);
  1003.     }
  1004.  
  1005.     n = 10; rxbytes = 0l;
  1006.  
  1007.     for (;;) {
  1008.         stohdr(rxbytes);
  1009.         zshhdr(ZRPOS, Txhdr);
  1010. nxthdr:
  1011.         switch (c = zgethdr(Rxhdr, 0)) {
  1012.         default:
  1013.             vfile("rzfile: zgethdr returned %d", c);
  1014.             return ERROR;
  1015.         case ZNAK:
  1016.         case TIMEOUT:
  1017.             if ( --n < 0) {
  1018.                 vfile("rzfile: zgethdr returned %d", c);
  1019.                 return ERROR;
  1020.             }
  1021.         case ZFILE:
  1022.             zrdata(secbuf, KSIZE);
  1023.             continue;
  1024.         case ZEOF:
  1025.             if (rclhdr(Rxhdr) != rxbytes) {
  1026.                 continue;
  1027.             }
  1028.             if (closeit()) {
  1029.                 tryzhdrtype = ZFERR;
  1030.                 vfile("rzfile: closeit returned <> 0");
  1031.                 return ERROR;
  1032.             }
  1033.             vfile("rzfile: normal EOF");
  1034.             return c;
  1035.         case ERROR:    /* Too much garbage in header search error */
  1036.             if ( --n < 0) {
  1037.                 vfile("rzfile: zgethdr returned %d", c);
  1038.                 return ERROR;
  1039.             }
  1040.             zmputs(Attn);
  1041.             continue;
  1042.         case ZDATA:
  1043.             if (rclhdr(Rxhdr) != rxbytes) {
  1044.                 if ( --n < 0) {
  1045.                     return ERROR;
  1046.                 }
  1047.                 zmputs(Attn);  continue;
  1048.             }
  1049. moredata:
  1050.             switch (c = zrdata(secbuf, KSIZE)) {
  1051.             case ZCAN:
  1052.                 vfile("rzfile: zgethdr returned %d", c);
  1053.                 return ERROR;
  1054.             case ERROR:    /* CRC error */
  1055.                 if ( --n < 0) {
  1056.                     vfile("rzfile: zgethdr returned %d", c);
  1057.                     return ERROR;
  1058.                 }
  1059.                 zmputs(Attn);
  1060.                 continue;
  1061.             case TIMEOUT:
  1062.                 if ( --n < 0) {
  1063.                     vfile("rzfile: zgethdr returned %d", c);
  1064.                     return ERROR;
  1065.                 }
  1066.                 continue;
  1067.             case GOTCRCW:
  1068.                 n = 10;
  1069.                 putsec(secbuf, Rxcount);
  1070.                 rxbytes += Rxcount;
  1071.                 stohdr(rxbytes);
  1072.                 zshhdr(ZACK, Txhdr);
  1073.                 goto nxthdr;
  1074.             case GOTCRCQ:
  1075.                 n = 10;
  1076.                 putsec(secbuf, Rxcount);
  1077.                 rxbytes += Rxcount;
  1078.                 stohdr(rxbytes);
  1079.                 zshhdr(ZACK, Txhdr);
  1080.                 goto moredata;
  1081.             case GOTCRCG:
  1082.                 n = 10;
  1083.                 putsec(secbuf, Rxcount);
  1084.                 rxbytes += Rxcount;
  1085.                 goto moredata;
  1086.             case GOTCRCE:
  1087.                 n = 10;
  1088.                 putsec(secbuf, Rxcount);
  1089.                 rxbytes += Rxcount;
  1090.                 goto nxthdr;
  1091.             }
  1092.         }
  1093.     }
  1094. }
  1095.  
  1096. /*
  1097.  * Send a string to the modem, processing for \336 (sleep 1 sec)
  1098.  *   and \335 (break signal)
  1099.  */
  1100. zmputs(s)
  1101. char *s;
  1102. {
  1103.     register c;
  1104.  
  1105.     while (*s) {
  1106.         switch (c = *s++) {
  1107.         case '\336':
  1108.             sleep(1); continue;
  1109.         case '\335':
  1110.             sendbrk(); continue;
  1111.         default:
  1112.             sendline(c);
  1113.         }
  1114.     }
  1115. }
  1116.  
  1117. /*
  1118.  * Close the receive dataset, return OK or ERROR
  1119.  */
  1120. closeit()
  1121. {
  1122.     if (Topipe) {
  1123.         if (pclose(fout)) {
  1124.             return ERROR;
  1125.         }
  1126.         return OK;
  1127.     }
  1128.     if (fclose(fout)==ERROR) {
  1129.         fprintf(stderr, "file close ERROR\n");
  1130.         return ERROR;
  1131.     }
  1132.     if (Modtime) {
  1133.         timep[0] = time(NULL);
  1134.         timep[1] = Modtime;
  1135.         utime(Pathname, timep);
  1136.     }
  1137.     if (Filemode)
  1138.         chmod(Pathname, (07777 & Filemode));
  1139.     return OK;
  1140. }
  1141.  
  1142. /*
  1143.  * Ack a ZFIN packet, let byegones be byegones
  1144.  */
  1145. ackbibi()
  1146. {
  1147.     register n;
  1148.  
  1149.     vfile("ackbibi:");
  1150.     Readnum = 1;
  1151.     stohdr(0L);
  1152.     for (n=3; --n>=0; ) {
  1153.         purgeline();
  1154.         zshhdr(ZFIN, Txhdr);
  1155.         switch (readline(100)) {
  1156.         case 'O':
  1157.             readline(1);    /* Discard 2nd 'O' */
  1158.             vfile("ackbibi complete");
  1159.             return;
  1160.         case RCDO:
  1161.             return;
  1162.         case TIMEOUT:
  1163.         default:
  1164.             break;
  1165.         }
  1166.     }
  1167. }
  1168.  
  1169.  
  1170.  
  1171. /*
  1172.  * Local console output simulation
  1173.  */
  1174. bttyout(c)
  1175. {
  1176.     if (Verbose || fromcu())
  1177.         putc(c, stderr);
  1178. }
  1179.  
  1180. /*
  1181.  * Strip leading ! if present, do shell escape. 
  1182.  */
  1183. sys2(s)
  1184. register char *s;
  1185. {
  1186.     if (*s == '!')
  1187.         ++s;
  1188.     return system(s);
  1189. }
  1190. /*
  1191.  * Strip leading ! if present, do exec.
  1192.  */
  1193. exec2(s)
  1194. register char *s;
  1195. {
  1196.     if (*s == '!')
  1197.         ++s;
  1198.     mode(0);
  1199.     execl("/bin/sh", "sh", "-c", s);
  1200. }
  1201. /* End of rz.c */
  1202.